﻿//
// (C) Copyright 2009 Irantha Suwandarathna (iranthas@hotmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if !SILVERLIGHT
using System.Data.Common;
using System.Data;
#endif
using EffiProz.Core.Errors;
using EffiProz.Core.Lib;

namespace EffiProz.Core.DataTypes
{
    public class CharacterType : SqlType
    {
        public Collation collation;
        public Charset charset;
        public bool _isEqualIdentical;
        public const int defaultCharPrecision = 32 * 1024;
        public const long maxCharPrecision = int.MaxValue;

        public CharacterType(Collation collation, int type, long precision)
            : base(Types.SQL_VARCHAR, type, precision, 0)
        {

            this.collation = collation;
            this.charset = Charset.getDefaultInstance();
            _isEqualIdentical = this.collation.isEqualAlwaysIdentical()
                               && type != Types.VARCHAR_IGNORECASE;
        }

        /**
         * Always English collation
         */
        public CharacterType(int type, long precision)
            : base(Types.SQL_VARCHAR, type, precision, 0)
        {

            this.collation = Collation.getDefaultInstance();
            this.charset = Charset.getDefaultInstance();
            _isEqualIdentical = type != Types.VARCHAR_IGNORECASE;
        }

        public override int displaySize()
        {
            return precision > int.MaxValue ? int.MaxValue
                                                 : (int)precision;
        }

        public override int getAdoTypeCode()
        {
#if !SILVERLIGHT
             switch (typeCode)
            {

                case Types.SQL_CHAR:
                    return (int)DbType.StringFixedLength;

                case Types.SQL_VARCHAR:
                case Types.VARCHAR_IGNORECASE:
                    return (int)DbType.String;

                case Types.SQL_CLOB:
                    return (int)DbType.String;

                default:
                    throw Error.runtimeError(ErrorCode.U_S0500, "CharacterType");
            }
#else
            return -1;
#endif
           
        }

        public override String getCSharpClassName()
        {
            return "System.String";
        }

        public override int getSQLGenericTypeCode()
        {
            return typeCode == Types.SQL_CHAR ? typeCode
                                              : Types.SQL_VARCHAR;
        }

        public override String getNameString()
        {

            switch (typeCode)
            {

                case Types.SQL_CHAR:
                    return Tokens.T_CHARACTER;

                case Types.SQL_VARCHAR:
                    return Tokens.T_VARCHAR;

                case Types.VARCHAR_IGNORECASE:
                    return Tokens.T_VARCHAR_IGNORECASE;

                case Types.SQL_CLOB:
                    return Tokens.T_CLOB;

                default:
                    throw Error.runtimeError(ErrorCode.U_S0500, "CharacterType");
            }
        }

        public override String getFullNameString()
        {

            switch (typeCode)
            {

                case Types.SQL_CHAR:
                    return Tokens.T_CHARACTER;

                case Types.SQL_VARCHAR:
                    return "CHARACTER VARYING";

                case Types.VARCHAR_IGNORECASE:
                    return Tokens.T_VARCHAR_IGNORECASE;

                case Types.SQL_CLOB:
                    return "CHARACTER LARGE OBJECT";

                default:
                    throw Error.runtimeError(ErrorCode.U_S0500, "CharacterType");
            }
        }

        public override String getDefinition()
        {

            if (precision == 0)
            {
                return getNameString();
            }

            StringBuilder sb = new StringBuilder(16);

            sb.Append(getNameString());
            sb.Append('(');
            sb.Append(precision);
            sb.Append(')');

            return sb.ToString();
        }

        public override bool isCharacterType()
        {
            return true;
        }

        public override long getMaxPrecision()
        {
            return maxCharPrecision;
        }

        public override bool acceptsPrecision()
        {
            return true;
        }

        public override bool requiresPrecision()
        {
            return typeCode == Types.SQL_VARCHAR
                   || typeCode == Types.VARCHAR_IGNORECASE;
        }

        public override int precedenceDegree(SqlType other)
        {

            if (other.typeCode == typeCode)
            {
                return 0;
            }

            if (!other.isCharacterType())
            {
                return int.MinValue;
            }

            switch (typeCode)
            {

                case Types.SQL_CHAR:
                    return other.typeCode == Types.SQL_CLOB ? 4
                                                            : 2;

                case Types.SQL_VARCHAR:
                case Types.VARCHAR_IGNORECASE:
                    return other.typeCode == Types.SQL_CLOB ? 4
                                                            : -2;

                case Types.SQL_CLOB:
                    return other.typeCode == Types.SQL_CHAR ? -4
                                                            : -2;

                default:
                    throw Error.runtimeError(ErrorCode.U_S0500, "CharacterType");
            }
        }

        public override SqlType getAggregateType(SqlType other)
        {

            if (typeCode == other.typeCode)
            {
                return precision >= other.precision ? this
                                                    : other;
            }

            switch (other.typeCode)
            {

                case Types.SQL_ALL_TYPES:
                    return this;

                case Types.SQL_CHAR:
                    return precision >= other.precision ? this
                                                        : getCharacterType(
                                                        typeCode, other.precision);

                case Types.SQL_VARCHAR:
                    if (typeCode == Types.SQL_CLOB
                            || typeCode == Types.VARCHAR_IGNORECASE)
                    {
                        return precision >= other.precision ? this
                                                            : getCharacterType(
                                                            typeCode,
                                                            other.precision);
                    }
                    else
                    {
                        return other.precision >= precision ? other
                                                            : getCharacterType(
                                                            other.typeCode,
                                                            precision);
                    }
                case Types.VARCHAR_IGNORECASE:
                    if (typeCode == Types.SQL_CLOB)
                    {
                        return precision >= other.precision ? this
                                                            : getCharacterType(
                                                            typeCode,
                                                            other.precision);
                    }
                    else
                    {
                        return other.precision >= precision ? other
                                                            : getCharacterType(
                                                            other.typeCode,
                                                            precision);
                    }
                case Types.SQL_CLOB:
                    return other.precision >= precision ? other
                                                        : getCharacterType(
                                                        other.typeCode, precision);

                case Types.SQL_BIT:
                case Types.SQL_BIT_VARYING:
                case Types.SQL_BLOB:
                case Types.SQL_BINARY:
                case Types.SQL_VARBINARY:
                case Types.OTHER:
                    throw Error.error(ErrorCode.X_42562);
                default:

                    /**
                     * @todo - this seems to be allowed in SQL-92 (is in NIST)
                     * but is disallowed in SQL:2003
                     * need to make dependent on a database property
                     */
                   
                    int length = other.displaySize();

                    return getCharacterType(Types.SQL_VARCHAR,
                                            length).getAggregateType(this);
                   
                    //throw Error.error(ErrorCode.X_42562);
            }
        }

        /**
         * For concatenation
         */
        public override SqlType getCombinedType(SqlType other, int operation)
        {

            if (operation != OpTypes.CONCAT)
            {
                return getAggregateType(other);
            }

            SqlType newType;
            long newPrecision = this.precision + other.precision;

            switch (other.typeCode)
            {

                case Types.SQL_ALL_TYPES:
                    return this;

                case Types.SQL_CHAR:
                    newType = this;
                    break;

                case Types.SQL_VARCHAR:
                    newType =
                        (typeCode == Types.SQL_CLOB || typeCode == Types
                            .VARCHAR_IGNORECASE) ? this
                                                 : other;
                    break;

                case Types.VARCHAR_IGNORECASE:
                    newType = typeCode == Types.SQL_CLOB ? this
                                                         : other;
                    break;

                case Types.SQL_CLOB:
                    newType = other;
                    break;

                default:
                    throw Error.error(ErrorCode.X_42562);
            }

            if (newPrecision > maxCharPrecision)
            {
                if (typeCode == Types.SQL_BINARY)
                {

                    // Standard disallows type length reduction
                    throw Error.error(ErrorCode.X_42570);
                }
                else if (typeCode == Types.SQL_CHAR)
                {
                    newPrecision = maxCharPrecision;
                }
            }

            return getCharacterType(newType.typeCode, precision + other.precision);
        }

        public override int compare(Session session, Object a, Object b)
        {

            if (a == b)
            {
                return 0;
            }

            if (a == null)
            {
                return -1;
            }

            if (b == null)
            {
                return 1;
            }

            String _as = (String)a;
            String _bs = (String)b;
            int la = _as.Length;
            int lb = _bs.Length;

            if (la == lb) { }
            else if (la > lb)
            {
                char[] buffer = new char[la];

                _bs.CopyTo(0, buffer, 0, lb);
                ArrayUtil.fillArray(buffer, lb, ' ');

                _bs = new String( buffer);
            }
            else
            {
                char[] buffer = new char[lb];

                _as.CopyTo(0, buffer, 0, la);
                ArrayUtil.fillArray(buffer, la, ' ');

                _as = new String(buffer);
            }

            if ((session != null && session.database != null && session.database.isIgnoreCase())
                || typeCode == Types.VARCHAR_IGNORECASE)
            {
                return collation.compareIgnoreCase(_as, _bs);
            }
            else
            {
                return collation.compare(_as, _bs);
            }
        }

        public override Object convertToTypeLimits(SessionInterface session, Object a)
        {

            if (a == null)
            {
                return a;
            }

            if (precision == 0)
            {
                return a;
            }

            switch (typeCode)
            {

                case Types.SQL_CHAR:
                    {
                        int slen = ((String)a).Length;

                        if (slen == precision)
                        {
                            return a;
                        }

                        if (slen > precision)
                        {
                            if (getRightTrimSise((String)a, ' ') <= precision)
                            {
                                return ((String)a).Substring(0, (int)precision);
                            }
                            else
                            {
                                throw Error.error(ErrorCode.X_22001);
                            }
                        }

                        char[] b = new char[(int)precision];

                        ((String)a).CopyTo(0, b, 0, slen);

                        for (int i = slen; i < precision; i++)
                        {
                            b[i] = ' ';
                        }

                        return new String(b);
                    }
                case Types.SQL_VARCHAR:
                case Types.VARCHAR_IGNORECASE:
                    {
                        int slen = ((String)a).Length;

                        if (slen > precision)
                        {
                            if (getRightTrimSise((String)a, ' ') <= precision)
                            {
                                return ((String)a).Substring(0, (int)precision);
                            }
                            else
                            {
                                throw Error.error(ErrorCode.X_22001);
                            }
                        }

                        return a;
                    }
                case Types.SQL_CLOB:

                    /** @todo implement */
                    return a;

                default:
                    throw Error.runtimeError(ErrorCode.U_S0500, "CharacterType");
            }
        }

        public override Object castToType(SessionInterface session, Object a,
                                 SqlType otherType)
        {

            if (a == null)
            {
                return a;
            }

            return castOrConvertToType(session, a, otherType, true);
        }

        public Object castOrConvertToType(SessionInterface session, Object a,
                                          SqlType otherType, bool cast)
        {

            switch (otherType.typeCode)
            {

                case Types.SQL_CHAR:
                case Types.SQL_VARCHAR:
                case Types.VARCHAR_IGNORECASE:
                    {
                        int length = ((String)a).Length;

                        if (precision != 0 && length > precision)
                        {
                            if (StringUtil.rightTrimSize((String)a) > precision)
                            {
                                if (!cast)
                                {
                                    throw Error.error(ErrorCode.X_22001);
                                }

                                session.addWarning(Error.error(ErrorCode.W_01004));
                            }

                            a = ((String)a).Substring(0, (int)precision);
                        }

                        switch (typeCode)
                        {

                            case Types.SQL_CHAR:
                                return convertToTypeLimits(session, a);

                            case Types.SQL_VARCHAR:
                            case Types.VARCHAR_IGNORECASE:
                                return a;

                            case Types.SQL_CLOB:
                                {
                                    ClobData clob =
                                        session.createClob(((String)a).Length);

                                    clob.setString(session, 0, (String)a);

                                    return clob;
                                }
                            default:
                                throw Error.runtimeError(ErrorCode.U_S0500,
                                                         "CharacterType");
                        }
                    }
                case Types.SQL_CLOB:
                    {
                        long length = ((ClobData)a).length(session);

                        if (precision != 0 && length > precision)
                        {
                            if (((ClobData)a).nonSpaceLength(session) > precision)
                            {
                                if (!cast)
                                {
                                    throw Error.error(ErrorCode.X_22001);
                                }

                                session.addWarning(Error.error(ErrorCode.W_01004));
                            }
                        }

                        switch (typeCode)
                        {

                            case Types.SQL_CHAR:
                            case Types.SQL_VARCHAR:
                            case Types.VARCHAR_IGNORECASE:
                                {
                                    if (length > maxCharPrecision)
                                    {
                                        if (!cast)
                                        {
                                            throw Error.error(ErrorCode.X_22001);
                                        }

                                        length = maxCharPrecision;
                                    }

                                    a = ((ClobData)a).getSubString(session, 0,
                                                                    (int)length);

                                    return convertToTypeLimits(session, a);
                                }
                            case Types.SQL_CLOB:
                                {
                                    if (precision != 0 && length > precision)
                                    {
                                        return ((ClobData)a).getClob(session, 0,
                                                                      precision);
                                    }

                                    return a;
                                }
                            default:
                                throw Error.runtimeError(ErrorCode.U_S0500,
                                                         "CharacterType");
                        }
                    }
                case Types.OTHER:
                    throw Error.error(ErrorCode.X_42561);
                case Types.SQL_BIT:
                case Types.SQL_BIT_VARYING:
                case Types.SQL_BLOB:
                case Types.SQL_BINARY:
                case Types.SQL_VARBINARY:
                default:
                    String s = otherType.convertToString(a);

                    if (precision != 0 && s.Length > precision)
                    {
                        throw Error.error(ErrorCode.X_22001);
                    }

                    a = s;

                    return convertToTypeLimits(session, a);
            }
        }

        public override Object convertToType(SessionInterface session, Object a,
                                    SqlType otherType)
        {

            if (a == null)
            {
                return a;
            }

            return castOrConvertToType(session, a, otherType, false);
        }

        public Object convertToTypeJDBC(SessionInterface session, Object a,
                                        SqlType otherType)
        {

            if (a == null)
            {
                return a;
            }

            return convertToType(session, a, otherType);
        }

        public override Object convertToDefaultType(SessionInterface session, Object a)
        {

            if (a == null)
            {
                return a;
            }

            if (a is Boolean)
            {
                return convertToType(session, a, SqlType.SQL_BOOLEAN);
            }
            else if (a is long)
            {
                a = ((long)a).ToString();

                return convertToType(session, a, SqlType.SQL_VARCHAR);
            }
            else if (a is int || a is long || a is short || a is byte || a is double || a is float || a is decimal || a is uint)
            {
                a = a.ToString();    // use shortcut

                return convertToType(session, a, SqlType.SQL_VARCHAR);
            }
            else if (a is String)
            {
                return convertToType(session, a, SqlType.SQL_VARCHAR);
            }
            else if (a is DateTime)
            {
                String s = ((DateTime)a).ToString();

                return convertToType(session, s, SqlType.SQL_VARCHAR);
            }
            else
            {
                throw Error.error(ErrorCode.X_42561);
            }
        }

        public override String convertToString(Object a)
        {

            if (a == null)
            {
                return null;
            }

            switch (typeCode)
            {

                case Types.SQL_CHAR:
                    {
                        int slen = ((String)a).Length;

                        if (precision == 0 || slen == precision)
                        {
                            return (String)a;
                        }

                        char[] b = new char[(int)precision];

                        ((String)a).CopyTo(0, b, 0, slen);

                        for (int i = slen; i < precision; i++)
                        {
                            b[i] = ' ';
                        }

                        return new String(b);
                    }
                case Types.SQL_VARCHAR:
                case Types.VARCHAR_IGNORECASE:
                    {
                        return (String)a;
                    }
                default:
                    throw Error.runtimeError(ErrorCode.U_S0500, "CharacterType");
            }
        }

        public override String convertToSQLString(Object a)
        {

            if (a == null)
            {
                return Tokens.T_NULL;
            }

            String s = convertToString(a);

            return StringConverter.toQuotedString(s, '\'', true);
        }

        public override bool canConvertFrom(SqlType otherType)
        {
            return !otherType.isObjectType();
        }

        public Collation getCollation()
        {
            return collation;
        }

        public Charset getCharacterSet()
        {
            return charset;
        }

        public bool isEqualIdentical()
        {
            return _isEqualIdentical;
        }

        public bool isCaseInsensitive()
        {
            return typeCode == Types.VARCHAR_IGNORECASE;
        }

        public virtual long position(SessionInterface session, Object data,
                             Object otherData, SqlType otherType, long offset)
        {

            if (data == null || otherData == null)
            {
                return -1L;
            }

            if (otherType.typeCode == Types.SQL_CLOB)
            {
                long otherLength = ((ClobData)data).length(session);

                if (offset + otherLength > ((String)data).Length)
                {
                    return -1;
                }

                String otherString = ((ClobData)otherData).getSubString(session,
                    0, (int)otherLength);

                return ((String)data).IndexOf(otherString, (int)offset);
            }
            else if (otherType.isCharacterType())
            {
                long otherLength = ((String)data).Length;

                if (offset + otherLength > ((String)data).Length)
                {
                    return -1;
                }

                return ((String)data).IndexOf((String)otherData, (int)offset);
            }
            else
            {
                throw Error.runtimeError(ErrorCode.U_S0500, "CharacterType");
            }
        }

        public Object substring(SessionInterface session, Object data,
                                long offset, long length, bool hasLength,
                                bool trailing)
        {

            long end;
            long dataLength = typeCode == Types.SQL_CLOB
                              ? ((ClobData)data).length(session)
                              : ((String)data).Length;

            if (trailing)
            {
                end = dataLength;

                if (length > dataLength)
                {
                    offset = 0;
                    length = dataLength;
                }
                else
                {
                    offset = dataLength - length;
                }
            }
            else if (hasLength)
            {
                end = offset + length;
            }
            else
            {
                end = dataLength > offset ? dataLength
                                          : offset;
            }

            if (end < offset)
            {
                throw Error.error(ErrorCode.X_22011);
            }

            if (offset > end || end < 0)
            {

                // return zero length data
                offset = 0;
                end = 0;
            }

            if (offset < 0)
            {
                offset = 0;
            }

            if (end > dataLength)
            {
                end = dataLength;
            }

            length = end - offset;

            if (data is String)
            {
                return ((String)data).Substring((int)offset,
                                                 (int)(length));
            }
            else if (data is ClobData)
            {
                ClobData clob = session.createClob(length);

                /** @todo - change to support long strings */
                String result = ((ClobData)data).getSubString(session, offset,
                    (int)length);

                clob.setString(session, 0, result);

                return clob;
            }
            else
            {
                throw Error.runtimeError(ErrorCode.U_S0500, "CharacterType");
            }
        }

        /**
         * Memory limits apply to Upper and Lower implementations with Clob data
         */
        public Object upper(Session session, Object data)
        {

            if (data == null)
            {
                return null;
            }

            if (typeCode == Types.SQL_CLOB)
            {
                String result = ((ClobData)data).getSubString(session, 0,
                    (int)((ClobData)data).length(session));

                result = collation.toUpperCase(result);

                ClobData clob = session.createClob(result.Length);

                clob.setString(session, 0, result);

                return clob;
            }

            return collation.toUpperCase((String)data);
        }

        public Object lower(Session session, Object data)
        {

            if (data == null)
            {
                return null;
            }

            if (typeCode == Types.SQL_CLOB)
            {
                String result = ((ClobData)data).getSubString(session, 0,
                    (int)((ClobData)data).length(session));

                result = collation.toLowerCase(result);

                ClobData clob = session.createClob(result.Length);

                clob.setString(session, 0, result);

                return clob;
            }

            return collation.toLowerCase((String)data);
        }

        public Object trim(SessionInterface session, Object data, int trim,
                           bool leading, bool trailing)
        {

            if (data == null)
            {
                return null;
            }

            String s;

            if (typeCode == Types.SQL_CLOB)
            {
                s = ((ClobData)data).getSubString(
                    session, 0, (int)((ClobData)data).length(session));
            }
            else
            {
                s = (String)data;
            }

            int endindex = s.Length;

            if (trailing)
            {
                for (--endindex; endindex >= 0 && s[(endindex)] == trim;
                        endindex--) { }

                endindex++;
            }

            int startindex = 0;

            if (leading)
            {
                while (startindex < endindex && s[(startindex)] == trim)
                {
                    startindex++;
                }
            }

            /** @todo - change to support long strings */
            if (startindex == 0 && endindex == s.Length) { }
            else
            {
                s = s.Substring(startindex, endindex- startindex);
            }

            if (typeCode == Types.SQL_CLOB)
            {
                ClobData clob = session.createClob(s.Length);

                clob.setString(session, 0, s);

                return clob;
            }
            else
            {
                return s;
            }
        }

        public Object overlay(SessionInterface session, Object data,
                              Object overlay, long offset, long length,
                              bool hasLength)
        {

            if (data == null || overlay == null)
            {
                return null;
            }

            if (!hasLength)
            {
                length = typeCode == Types.SQL_CLOB
                         ? ((ClobData)overlay).length(session)
                         : ((String)overlay).Length;
            }

            Object temp = concat(null,
                                 substring(session, data, 0, offset, true, false),
                                 overlay);

            return concat(null, temp,
                          substring(session, data, offset + length, 0, false,
                                    false));
        }

        public override Object concat(Session session, Object a, Object b)
        {

            if (a == null || b == null)
            {
                return null;
            }

            String left;
            String right;

            if (a is ClobData)
            {
                left = ((ClobData)a).getSubString(
                    session, 0, (int)((ClobData)a).length(session));
            }
            else
            {
                left = Convert.ToString(a);
            }

            if (b is ClobData)
            {
                right = ((ClobData)b).getSubString(
                    session, 0, (int)((ClobData)b).length(session));
            }
            else
            {
                right = Convert.ToString(b);
            }

            if (typeCode == Types.SQL_CLOB)
            {
                ClobData clob = session.createClob(left.Length + right.Length);

                clob.setString(session, 0, left);
                clob.setString(session, left.Length, right);

                return clob;
            }
            else
            {
                return left + right;
            }
        }

        public long size(SessionInterface session, Object data)
        {

            if (typeCode == Types.SQL_CLOB)
            {
                return ((ClobData)data).length(session);
            }

            return ((String)data).Length;
        }

        /*
            public static Object concat(Object a, Object b) {

                if (a == null || b == null) {
                    return null;
                }

                return a.ToString() + b.ToString();
            }
        */
        public static int getRightTrimSise(String s, char trim)
        {

            int endindex = s.Length;

            for (--endindex; endindex >= 0 && s[(endindex)] == trim;
                    endindex--) { }

            endindex++;

            return endindex;
        }

        public static SqlType getCharacterType(int type, long precision)
        {

            switch (type)
            {

                case Types.SQL_VARCHAR:
                case Types.SQL_CHAR:
                case Types.VARCHAR_IGNORECASE:
                    return new CharacterType(type, (int)precision);

                case Types.SQL_CLOB:
                    return new ClobType(precision);

                default:
                    throw Error.runtimeError(ErrorCode.U_S0500, "CharacterType");
            }
        }
    }
}
